home *** CD-ROM | disk | FTP | other *** search
/ SVM Mac 58 / CD-ROM N°58.iso / navigateurs / Netscape Folder / chrome / navigator / content / default / nsContextMenu.js < prev    next >
Encoding:
JavaScript  |  2000-04-19  |  18.3 KB  |  452 lines  |  [TEXT/MOSS]

  1. /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /*
  3.  * The contents of this file are subject to the Netscape Public
  4.  * License Version 1.1 (the "License"); you may not use this file
  5.  * except in compliance with the License. You may obtain a copy of
  6.  * the License at http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the License is distributed on an "AS
  9.  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  10.  * implied. See the License for the specific language governing
  11.  * rights and limitations under the License.
  12.  *
  13.  * The Original Code is Mozilla Communicator client code, 
  14.  * released March 31, 1998. 
  15.  *
  16.  * The Initial Developer of the Original Code is Netscape Communications 
  17.  * Corporation.  Portions created by Netscape are
  18.  * Copyright (C) 1998 Netscape Communications Corporation. All
  19.  * Rights Reserved.
  20.  *
  21.  * Contributor(s): 
  22.  *     William A. ("PowerGUI") Law <law@netscape.com>
  23.  */
  24.  
  25. /*------------------------------ nsContextMenu ---------------------------------
  26. |   This JavaScript "class" is used to implement the browser's content-area    |
  27. |   context menu.                                                              |
  28. |                                                                              |
  29. |   For usage, see references to this class in navigator.xul.                  |
  30. |                                                                              |
  31. |   Currently, this code is relatively useless for any other purpose.  In the  |
  32. |   longer term, this code will be restructured to make it more reusable.      |
  33. ------------------------------------------------------------------------------*/
  34. function nsContextMenu( xulMenu ) {
  35.     this.target     = null;
  36.     this.menu       = null;
  37.     this.onImage    = false;
  38.     this.onLink     = false;
  39.     this.onSaveableLink = false;
  40.     this.link       = false;
  41.     this.inFrame    = false;
  42.     this.hasBGImage = false;
  43.  
  44.     // Initialize new menu.
  45.     this.initMenu( xulMenu );
  46. }
  47.  
  48. // Prototype for nsContextMenu "class."
  49. nsContextMenu.prototype = {
  50.     // onDestroy is a no-op at this point.
  51.     onDestroy : function () {
  52.     },
  53.     // Initialize context menu.
  54.     initMenu : function ( popup, event ) {
  55.         // Save menu.
  56.         this.menu = popup;
  57.  
  58.         // Get contextual info.
  59.         this.setTarget( document.popupNode );
  60.     
  61.         // Initialize (disable/remove) menu items.
  62.         this.initItems();
  63.     },
  64.     initItems : function () {
  65.         this.initOpenItems();
  66.         this.initNavigationItems();
  67.         this.initViewItems();
  68.         this.initMiscItems();
  69.         this.initSaveItems();
  70.         this.initClipboardItems();
  71.     },
  72.     initOpenItems : function () {
  73.         // Remove open/edit link if not applicable.
  74.         this.showItem( "context-openlink", this.onSaveableLink );
  75.         this.showItem( "context-editlink", this.onSaveableLink );
  76.     
  77.         // Remove open frame if not applicable.
  78.         this.showItem( "context-openframe", this.inFrame );
  79.     
  80.         // Remove separator after open items if neither link nor frame.
  81.         this.showItem( "context-sep-open", this.onSaveableLink || this.inFrame );
  82.     },
  83.     initNavigationItems : function () {
  84.         // Back determined by canGoBack broadcaster.
  85.         this.setItemAttrFromNode( "context-back", "disabled", "canGoBack" );
  86.     
  87.         // Forward determined by canGoForward broadcaster.
  88.         this.setItemAttrFromNode( "context-forward", "disabled", "canGoForward" );
  89.     
  90.         // Reload is always OK.
  91.     
  92.         // Stop determined by canStop broadcaster.
  93.         this.setItemAttrFromNode( "context-stop", "disabled", "canStop" );
  94.     },
  95.     initSaveItems : function () {
  96.         // Save page is always OK.
  97.     
  98.         // Save frame as depends on whether we're in a frame.
  99.         this.showItem( "context-saveframe", this.inFrame );
  100.     
  101.         // Save link depends on whether we're in a link.
  102.         this.showItem( "context-savelink", this.onSaveableLink );
  103.     
  104.         // Save background image depends on whether there is one.
  105.         this.showItem( "context-savebgimage", this.hasBGImage );
  106.     
  107.         // Save image depends on whether there is one.
  108.         this.showItem( "context-saveimage", this.onImage );
  109.     },
  110.     initViewItems : function () {
  111.         // View source is always OK.
  112.     
  113.         // View frame source depends on whether we're in a frame.
  114.         this.showItem( "context-viewframesource", this.inFrame );
  115.     
  116.         // View Info don't work no way no how.
  117.         this.showItem( "context-viewinfo", false );
  118.     
  119.         // View Frame Info isn't working, either.
  120.         this.showItem( "context-viewframeinfo", false );
  121.     
  122.         // View Image depends on whether an image was clicked on.
  123.         this.showItem( "context-viewimage", this.onImage );
  124.     },
  125.     initMiscItems : function () {
  126.         // Add bookmark always OK.
  127.     
  128.         // Send Page not working yet.
  129.         this.showItem( "context-sendpage", false );
  130.     },
  131.     initClipboardItems : function () {
  132.         // Select All is always OK.
  133.     
  134.         // Copy depends on whether there is selected text.
  135.         // Enabling this context menu item is now done through the global
  136.         // command updating system
  137.         // this.setItemAttr( "context-copy", "disabled", this.isNoTextSelected() );
  138.     
  139.         // Copy link location depends on whether we're on a link.
  140.         this.showItem( "context-copylink", this.onLink );
  141.     
  142.         // Copy image location depends on whether we're on an image.
  143.         this.showItem( "context-copyimage", this.onImage );
  144.     },
  145.     // Set various context menu attributes based on the state of the world.
  146.     setTarget : function ( node ) {
  147.         // Initialize contextual info.
  148.         this.onImage    = false;
  149.         this.imageURL   = "";
  150.         this.onLink     = false;
  151.         this.inFrame    = false;
  152.         this.hasBGImage = false;
  153.     
  154.         // Remember the node that was clicked.
  155.         this.target = node;
  156.     
  157.         // See if the user clicked on an image.
  158.         if ( this.target.nodeType == 1 ) {
  159.              if ( this.target.tagName.toUpperCase() == "IMG" ) {
  160.                 this.onImage = true;
  161.                 this.imageURL = this.target.src;
  162.                 // Look for image map.
  163.                 var mapName = this.target.getAttribute( "usemap" );
  164.                 if ( mapName ) {
  165.                     // Find map.
  166.                     var map = this.target.ownerDocument.getElementById( mapName.substr(1) );
  167.                     if ( map ) {
  168.                         // Search child <area>s for a match.
  169.                         var areas = map.childNodes;
  170.                         //XXX Client side image maps are too hard for now!
  171.                         dump( "Client side image maps not supported yet, sorry!\n" );
  172.                         areas.length = 0;
  173.                         for ( var i = 0; i < areas.length && !this.onLink; i++ ) {
  174.                             var area = areas[i];
  175.                             if ( area.nodeType == 1
  176.                                  &&
  177.                                  area.tagName.toUpperCase() == "AREA" ) {
  178.                                 // Get type (rect/circle/polygon/default).
  179.                                 var type = area.getAttribute( "type" );
  180.                                 var coords = this.parseCoords( area );
  181.                                 switch ( type.toUpperCase() ) {
  182.                                     case "RECT":
  183.                                     case "RECTANGLE":
  184.                                         break;
  185.                                     case "CIRC":
  186.                                     case "CIRCLE":
  187.                                         break;
  188.                                     case "POLY":
  189.                                     case "POLYGON":
  190.                                         break;
  191.                                     case "DEFAULT":
  192.                                         // Default matches entire image.
  193.                                         this.onLink = true;
  194.                                         this.link = area;
  195.                                         this.onSaveableLink = this.isLinkSaveable( this.link );
  196.                                         break;
  197.                                 }
  198.                             }
  199.                         }
  200.                     }
  201.                 }   
  202.              } else if (this.target.tagName.toUpperCase() == "INPUT") {
  203.                if(this.target.getAttribute( "type" ).toUpperCase() == "IMAGE") {
  204.                  this.onImage = true;
  205.                  this.imageURL = this.target.src;
  206.                }
  207.              } else if (this.target.getAttribute( "background" )) {
  208.                this.onImage = true;
  209.                this.imageURL = this.target.getAttribute( "background" );
  210.              } else  {
  211.                var cssAttr = this.target.style.getPropertyValue( "list-style-image" ) ||
  212.                              this.target.style.getPropertyValue( "list-style" ) || 
  213.                              this.target.style.getPropertyValue( "background-image" ) || 
  214.                              this.target.style.getPropertyValue( "background" );
  215.                if ( cssAttr ) {
  216.                  this.onImage = true;
  217.                  this.imageURL = cssAttr.toLowerCase().replace(/url\("*(.+)"*\)/, "$1");
  218.                }
  219.              }
  220.              
  221.         }
  222.     
  223.         // See if the user clicked in a frame.
  224.         if ( this.target.ownerDocument != window.content.document ) {
  225.             this.inFrame = true;
  226.         }
  227.     
  228.         // Bubble out, looking for link.
  229.         var elem = this.target;
  230.         while ( elem && !this.onLink ) {
  231.             // Test for element types of interest.
  232.             if ( elem.nodeType == 1 && 
  233.                  ( elem.tagName.toUpperCase() == "A"
  234.                    ||
  235.                    elem.tagName.toUpperCase() == "AREA" ) ) {
  236.                 // Clicked on a link.
  237.                 this.onLink = true;
  238.                 // Remember corresponding element.
  239.                 this.link = elem;
  240.                 // Remember if it is saveable.
  241.                 this.onSaveableLink = this.isLinkSaveable( this.link );
  242.             }
  243.             elem = elem.parentNode;
  244.         }
  245.     },
  246.     // Returns true iff clicked on link is saveable.
  247.     isLinkSaveable : function ( link ) {
  248.         // Test for missing protocol property.
  249.         if ( !link.protocol ) {
  250.            // We must resort to testing the URL string :-(.
  251.            dump( "Bug! Link.protocol is still undefined!\n" );
  252.            var protocol = link.href.substr( 0, 11 );
  253.            return protocol != "javascript:";
  254.         } else {
  255.            // Presume all but javascript: urls are saveable.
  256.            return link.protocol != "javascript:";
  257.         }
  258.     },
  259.     // Open linked-to URL in a new window.
  260.     openLink : function () {
  261.         // Determine linked-to URL.
  262.         openNewWindowWith( this.linkURL() );
  263.     },
  264.     // Edit linked-to URL in a new window.
  265.     editLink : function () {
  266.         BrowserEditPage( this.linkURL() );
  267.     },
  268.     // Open clicked-in frame in its own window.
  269.     openFrame : function () {
  270.         openNewWindowWith( this.target.ownerDocument.location.href );
  271.     },
  272.     // Open new "view source" window with the frame's URL.
  273.     viewFrameSource : function () {
  274.         window.openDialog( "chrome://navigator/content/",
  275.                            "_blank",
  276.                            "chrome,menubar,status,dialog=no", 
  277.                            this.target.ownerDocument.location.href,
  278.                            "view-source" );
  279.     },
  280.     viewInfo : function () {
  281.         dump( "nsContextMenu.viewInfo not implemented yet\n" );
  282.     },
  283.     viewFrameInfo : function () {
  284.         dump( "nsContextMenu.viewFrameInfo not implemented yet\n" );
  285.     },
  286.     // Open new window with the URL of the image.
  287.     viewImage : function () {
  288.         openNewWindowWith( this.imageURL );
  289.     },
  290.     // Save URL of clicked-on frame.
  291.     saveFrame : function () {
  292.         this.savePage( this.target.ownerDocument.location.href );
  293.     },
  294.     // Save URL of clicked-on link.
  295.     saveLink : function () {
  296.         this.savePage( this.linkURL() );
  297.     },
  298.     // Save URL of clicked-on image.
  299.     saveImage : function () {
  300.         this.savePage( this.imageURL );
  301.     },
  302.     // Save URL of background image.
  303.     saveBGImage : function () {
  304.         this.savePage( this.bgImageURL() );
  305.     },
  306.     // Generate link URL and put it on clibboard.
  307.     copyLink : function () {
  308.         this.copyToClipboard( this.linkURL() );
  309.     },
  310.     // Generate image URL and put it on the clipboard.
  311.     copyImage : function () {
  312.         this.copyToClipboard( this.imageURL );
  313.     },
  314.     // Utilities
  315.     // Show/hide one item (specified via name or the item element itself).
  316.     showItem : function ( itemOrId, show ) {
  317.         var item = null;
  318.         if ( itemOrId.constructor == String ) {
  319.             // Argument specifies item id.
  320.             item = document.getElementById( itemOrId );
  321.         } else {
  322.             // Argument is the item itself.
  323.             item = itemOrId;
  324.         }
  325.         if ( item ) {
  326.             var styleIn = item.getAttribute( "style" );
  327.             var styleOut = styleIn;
  328.             if ( show ) {
  329.                 // Remove style="display:none;".
  330.                 styleOut = styleOut.replace( "display:none;", "" );
  331.  
  332.             } else {
  333.                 // Set style="display:none;".
  334.                 if ( styleOut.indexOf( "display:none;" ) == -1 ) {
  335.                     // Add style the first time we need to.
  336.                     styleOut += "display:none;";
  337.                 }
  338.             }
  339.             // Only set style if it's different.
  340.             if ( styleIn != styleOut ) {
  341.                 item.setAttribute( "style", styleOut );
  342.             }
  343.         }
  344.     },
  345.     // Set given attribute of specified context-menu item.  If the
  346.     // value is null, then it removes the attribute (which works
  347.     // nicely for the disabled attribute).
  348.     setItemAttr : function ( id, attr, val ) {
  349.         var elem = document.getElementById( id );
  350.         if ( elem ) {
  351.             if ( val == null ) {
  352.                 // null indicates attr should be removed.
  353.                 elem.removeAttribute( attr );
  354.             } else {
  355.                 // Set attr=val.
  356.                 elem.setAttribute( attr, val );
  357.             }
  358.         }
  359.     },
  360.     // Set context menu attribute according to like attribute of another node
  361.     // (such as a broadcaster).
  362.     setItemAttrFromNode : function ( item_id, attr, other_id ) {
  363.         var elem = document.getElementById( other_id );
  364.         if ( elem && elem.getAttribute( attr ) == "true" ) {
  365.             this.setItemAttr( item_id, attr, "true" );
  366.         } else {
  367.             this.setItemAttr( item_id, attr, null );
  368.         }
  369.     },
  370.     // Temporary workaround for DOM api not yet implemented by XUL nodes.
  371.     cloneNode : function ( item ) {
  372.         // Create another element like the one we're cloning.
  373.         var node = document.createElement( item.tagName );
  374.     
  375.         // Copy attributes from argument item to the new one.
  376.         var attrs = item.attributes;
  377.         for ( var i = 0; i < attrs.length; i++ ) {
  378.             var attr = attrs.item( i );
  379.             node.setAttribute( attr.nodeName, attr.nodeValue );
  380.         }
  381.     
  382.         // Voila!
  383.         return node;
  384.     },
  385.     // Generate fully-qualified URL for clicked-on link.
  386.     linkURL : function () {
  387.         return this.link.href;
  388.     },
  389.     // Returns "true" if there's no text selected, null otherwise.
  390.     isNoTextSelected : function ( event ) {
  391.         // Not implemented so all text-selected-based options are disabled.
  392.         return "true";
  393.     },
  394.     // Copy link/image url to clipboard.
  395.     copyToClipboard : function ( text ) {
  396.         // Get clipboard.
  397.         var clipboard = Components
  398.                           .classes["component://netscape/widget/clipboard"]
  399.                             .getService ( Components.interfaces.nsIClipboard );
  400.         // Create tranferable that will transfer the text.
  401.         var transferable = Components
  402.                              .classes["component://netscape/widget/transferable"]
  403.                                .createInstance( Components.interfaces.nsITransferable );
  404.         if ( clipboard && transferable ) {
  405.           transferable.addDataFlavor( "text/unicode" );
  406.           // Create wrapper for text.
  407.           var data = createInstance( "component://netscape/supports-wstring",
  408.                                       "nsISupportsWString" );
  409.           if ( data ) {
  410.             data.data = text ;
  411.             transferable.setTransferData( "text/unicode", data, text.length * 2 );
  412.             // Put on clipboard.
  413.             clipboard.setData( transferable, null );
  414.           }
  415.         }
  416.     },
  417.     // Save specified URL in user-selected file.
  418.     savePage : function ( url ) {
  419.         // Default is to save current page.
  420.         if ( !url ) {
  421.             url = window.content.location.href;
  422.         }
  423.         // Use stream xfer component to prompt for destination and save.
  424.         var xfer = Components
  425.                      .classes[ "component://netscape/appshell/component/xfer" ]
  426.                        .getService( Components.interfaces.nsIStreamTransfer );
  427.         try {
  428.             // When Necko lands, we need to receive the real nsIChannel and
  429.             // do SelectFileAndTransferLocation!
  430.  
  431.             // Use this for now...
  432.             xfer.SelectFileAndTransferLocationSpec( url, window );
  433.         } catch( exception ) {
  434.             // Failed (or cancelled), give them another chance.
  435.             dump( "SelectFileAndTransferLocationSpec failed, rv=" + exception + "\n" );
  436.         }
  437.         return;
  438.     },
  439.     // Parse coords= attribute and return array.
  440.     parseCoords : function ( area ) {
  441.         return [];
  442.     },
  443.     toString : function () {
  444.         return "contextMenu.target     = " + this.target + "\n" +
  445.                "contextMenu.onImage    = " + this.onImage + "\n" +
  446.                "contextMenu.onLink     = " + this.onLink + "\n" +
  447.                "contextMenu.link       = " + this.link + "\n" +
  448.                "contextMenu.inFrame    = " + this.inFrame + "\n" +
  449.                "contextMenu.hasBGImage = " + this.hasBGImage + "\n";
  450.     }
  451. };
  452.